/*
Copyright 2024.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package controller

import (
	"context"
	"fmt"

	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/log"

	webappv1 "helloworld.practice/api/v1"
)

// WelcomeReconciler reconciles a Welcome object
type WelcomeReconciler struct {
	client.Client
	Scheme *runtime.Scheme
}

//+kubebuilder:rbac:groups=webapp.helloworld.practice,resources=welcomes,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=webapp.helloworld.practice,resources=welcomes/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=webapp.helloworld.practice,resources=welcomes/finalizers,verbs=update

// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// TODO(user): Modify the Reconcile function to compare the state specified by
// the Welcome object against the actual cluster state, and then
// perform operations to make the cluster state reflect the state specified by
// the user.
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.17.3/pkg/reconcile

// 响应资源的增删改；查看资源，并对资源做出增删改等动作。
func (r *WelcomeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
	logger := log.FromContext(ctx)
	// TODO(user): your logic here
	webapp := &webappv1.Welcome{}

	// 查看资源1：查看特定资源
	if err := r.Get(ctx, req.NamespacedName, webapp); err != nil {
		if errors.IsNotFound(err) {
			// 请求的对象不存在，可能是已被删除
			logger.Info("Welcom resource not found. Ignoring since object must be deleted.")
			return ctrl.Result{}, nil
		}

		// 错误不是 "NotFound"，返回错误
		logger.Error(err, "Failed to get Welcom")
		return ctrl.Result{}, err

	}

	fmt.Println("Greeting from Kubebuilder to ", (*webapp).Spec.Name)

	//查看资源2：list同一类型所有资源
	r.GetAllPods(ctx, req)

	// 增：创建deployment
	r.CreateDeployment(ctx, req)

	// 增：创建service服务，方便通过RESTful api进行访问
	r.CreateService(ctx, req)

	// 删：删除资源
	r.RemoveThePod(ctx, req)

	// 改：修改资源的成员
	// 注意：更新状态通常是一个幂等操作，所以即使多次调用也不会有问题
	webapp.Status.Status = "Running"
	if err := r.Status().Update(ctx, webapp); err != nil {
		logger.Error(err, "unable to update status")
	}

	return ctrl.Result{}, nil
}

// SetupWithManager sets up the controller with the Manager.
func (r *WelcomeReconciler) SetupWithManager(mgr ctrl.Manager) error {
	return ctrl.NewControllerManagedBy(mgr).
		For(&webappv1.Welcome{}).
		Complete(r)
}

func (r *WelcomeReconciler) GetAllPods(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
	reqLogger := log.FromContext(ctx)
	reqLogger.Info("create deployment")

	// List all Pods in the cluster
	pods := &corev1.PodList{}
	if err := r.Client.List(ctx, pods); err != nil {
		reqLogger.Error(err, "Failed to list pods")
		return ctrl.Result{}, err
	}

	// Now you have all the pods in the cluster, you can iterate over them
	for _, pod := range pods.Items {
		reqLogger.Info("Found pod", "Pod.Name", pod.Name)
		// Do something with the pod...
	}

	// Your reconciliation logic here...

	return ctrl.Result{}, nil
}

// 创建一个deployment
func (r *WelcomeReconciler) CreateDeployment(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
	logger := log.FromContext(ctx)
	dep := &appsv1.Deployment{}
	namespacedName := types.NamespacedName{Name: "helloworld-deployment", Namespace: "default"}
	if err := r.Get(ctx, namespacedName, dep); err == nil {
		// 该deployment已经存在，先删除它
		fmt.Printf("Deployment %s/%s existed，remove it\n", dep.Namespace, dep.Name)
		r.Client.Delete(ctx, dep, &client.DeleteOptions{})
	}

	// 创建一个新的Deployment对象
	dep = &appsv1.Deployment{
		ObjectMeta: metav1.ObjectMeta{
			Name:      namespacedName.Name,
			Namespace: namespacedName.Namespace,
		},
		Spec: appsv1.DeploymentSpec{
			Replicas: int32Ptr(1), // 设置副本数为1
			Selector: &metav1.LabelSelector{
				MatchLabels: map[string]string{"app": "webserver"},
			},
			Template: corev1.PodTemplateSpec{
				ObjectMeta: metav1.ObjectMeta{
					Labels: map[string]string{"app": "webserver"},
				},
				Spec: corev1.PodSpec{
					Containers: []corev1.Container{
						{
							Name:  "my-container",
							Image: "webservice:v1",
							Ports: []corev1.ContainerPort{
								{
									ContainerPort: 9090,
								},
							},
						},
					},
				},
			},
		},
	}
	// 使用client-go将Deployment应用到集群中
	err := r.Client.Create(ctx, dep)
	if err != nil && !errors.IsAlreadyExists(err) {
		// 如果Deployment已经存在，我们可能想要忽略这个错误
		// 但是对于其他错误，我们应该返回并报告
		logger.Error(err, "fail to create Deployment")
		return ctrl.Result{}, err
	}

	fmt.Printf("Deployment %s/%s created\n", dep.Namespace, dep.Name)

	return ctrl.Result{}, nil
}

// 创建service资源
func (r *WelcomeReconciler) CreateService(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
	logger := log.FromContext(ctx)
	svc := &corev1.Service{}
	namespacedName := types.NamespacedName{Name: "helloworld-service", Namespace: "default"}
	if err := r.Get(ctx, namespacedName, svc); err == nil {
		// 该Service已经存在，先删除它
		fmt.Printf("Deployment %s/%s existed，remove it\n", svc.Namespace, svc.Name)
		r.Client.Delete(ctx, svc, &client.DeleteOptions{})
	}

	svc = &corev1.Service{
		TypeMeta:   metav1.TypeMeta{APIVersion: corev1.SchemeGroupVersion.Version, Kind: "Service"},
		ObjectMeta: metav1.ObjectMeta{Name: namespacedName.Name, Namespace: namespacedName.Namespace},
		Spec: corev1.ServiceSpec{
			Ports: []corev1.ServicePort{
				{Name: "http", Port: 9090, Protocol: "TCP"},
			},
			Selector: map[string]string{"app": "webserver"},
			Type:     corev1.ServiceTypeNodePort,
		},
	}

	// 创建一个新的Service对象
	err := r.Client.Create(ctx, svc)
	if err != nil && !errors.IsAlreadyExists(err) {
		// 如果Service已经存在，我们可能想要忽略这个错误
		// 但是对于其他错误，我们应该返回并报告
		logger.Error(err, "fail to create Service")
		return ctrl.Result{}, err
	}
	fmt.Printf("Service %s/%s created\n", svc.Namespace, svc.Name)

	return ctrl.Result{}, nil
}

// int32Ptr 将int32转换为*int32
func int32Ptr(i int32) *int32 { return &i }

func (r *WelcomeReconciler) RemoveThePod(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
	reqLogger := log.FromContext(ctx)
	reqLogger.Info("remove the Pod", "Pod.Namespace", req.NamespacedName, "Pod.Name", req.Name)

	// Assume you have a condition to check if the Pod should be deleted
	shouldDelete := true // This should be based on some condition in your custom resource

	if shouldDelete {
		// Create a Pod object with the name and namespace to delete
		pod := &corev1.Pod{
			ObjectMeta: metav1.ObjectMeta{
				Name:      req.Name,
				Namespace: req.Namespace,
			},
		}

		// Delete the Pod
		if err := r.Client.Delete(ctx, pod, &client.DeleteOptions{}); err != nil {
			if errors.IsNotFound(err) {
				// Pod does not exist - don't requeue
				reqLogger.Info("Pod does not exist", "Pod.Namespace", req.Namespace, "Pod.Name", req.Name)
				return ctrl.Result{}, nil
			}
			// Error deleting the Pod - requeue the request
			reqLogger.Error(err, "Failed to delete Pod", "Pod.Namespace", req.Namespace, "Pod.Name", req.Name)
			return ctrl.Result{}, err
		}

		// Pod deleted successfully
		reqLogger.Info("Pod deleted", "Pod.Namespace", req.Namespace, "Pod.Name", req.Name)
	}

	// Your other reconciliation logic here...

	return ctrl.Result{}, nil
}
